
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class DateUtil {
    private static final String EMPTY_STRING = "";
    public static final String FORMAT_YMD = "yyyy-MM-dd";
    public static final String FORMAT_YMDH = "yyyy-MM-dd HH";
    public static final String FORMAT_YMDHM = "yyyy-MM-dd HH:mm";
    public static final String FORMAT_YMDHMS = "yyyy-MM-dd HH:mm:ss";
    public static final String FORMAT_CN_YMDHMS = "yyyy年MM月dd日 HH:mm:ss";
    public static final String FORMAT_CN_YMDHM = "yyyy年MM月dd日 HH:mm";
    public static final String FORMAT_CN_YMD = "yyyy年MM月dd日";
    public static final String FORMAT_CN_MD = "MM月dd日";
    public static final String FORMAT_HM = "HH:mm";
    public static final String FORMAT_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    public static final int ONE_SECOND = 1000;
    public static final int ONE_MINUTE = 60000;
    public static final int ONE_HOUR = 3600000;
    public static final int ONE_DAY = 86400000;
    public static final long ONE_MONTH = 2592000000L;
    public static final long ONE_YEAR = 31104000000L;

    // 使用 ThreadLocal 存储 SimpleDateFormat，保证线程安全
    private static ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>() {
        /**
         * 初始化 SimpleDateFormat 对象
         * @return 新的 SimpleDateFormat 对象
         */
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat();
        }
    };

    private DateUtil() {}

    /**
     * 按照指定格式格式化日期对象
     * @param date 日期对象
     * @param format 日期格式
     * @return 格式化后的日期字符串
     */
    public static String formatDate(Date date, String format) {
        if (date == null) {
            return "";
        } else {
            sdf.get().applyPattern(format);
            return sdf.get().format(date);
        }
    }

    /**
     * 按照指定格式解析日期字符串为日期对象
     * @param date 日期字符串
     * @param format 日期格式
     * @return 解析后的日期对象，如果解析失败返回 null
     */
    public static Date parseDate(String date, String format) {
        sdf.get().applyPattern(format);

        try {
            return sdf.get().parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将 UTC 日期字符串转换为本地日期对象
     * @param utcDate UTC 日期字符串，格式为 yyyy-MM-dd'T'HH:mm:ss'Z'
     * @return 本地日期对象，如果解析失败返回 null
     */
    public static Date parseUTC2Local(String utcDate) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date utc = null;

        try {
            utc = sdf.parse(utcDate);
            return utc;
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将网页日期格式转换为数据库日期格式
     * @param date 网页日期字符串
     * @return 数据库日期字符串
     */
    public static String parseWebDate2DbDate(String date) {
        try {
            if (date.contains("年") && date.contains("月") && date.contains("日")) {
                if (date.contains(":")) {
                    sdf.get().applyPattern(DateUtil.FORMAT_CN_YMDHM);
                    return formatDate(sdf.get().parse(date), DateUtil.FORMAT_YMDHM);
                }

                sdf.get().applyPattern(DateUtil.FORMAT_CN_YMD);
                return formatDate(sdf.get().parse(date), DateUtil.FORMAT_YMD);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return date;
    }

    /**
     * 将数据库日期格式转换为网页日期格式
     * @param date 数据库日期字符串
     * @return 网页日期字符串
     */
    public static String parseDBDate2WebDate(String date) {
        try {
            sdf.get().applyPattern(DateUtil.FORMAT_YMD);
            return formatDate(sdf.get().parse(date), DateUtil.FORMAT_CN_YMD);
        } catch (ParseException e) {
            e.printStackTrace();
            return date;
        }
    }

    /**
     * 将数据库日期时间格式转换为网页日期时间格式
     * @param date 数据库日期时间字符串
     * @return 网页日期时间字符串
     */
    public static String parseDBDateTime2WebDateTime(String date) {
        try {
            if (date.contains(":")) {
                sdf.get().applyPattern(DateUtil.FORMAT_YMDHMS);
                return formatDate(sdf.get().parse(date), DateUtil.FORMAT_CN_YMDHMS);
            } else {
                sdf.get().applyPattern(DateUtil.FORMAT_YMD);
                return formatDate(sdf.get().parse(date), DateUtil.FORMAT_CN_YMD);
            }
        } catch (ParseException e) {
            e.printStackTrace();
            return date;
        }
    }


    /**
     * 将本地日期对象格式化为 UTC 日期字符串
     * @param localDate 本地日期对象
     * @return UTC 日期字符串，格式为 yyyy-MM-dd'T'HH:mm:ss'Z'
     */
    public static String formatLocal2UTC(Date localDate) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        return sdf.format(localDate);
    }

    /**
     * 将日期对象格式化为网页日期格式，格式为 yyyy年MM月dd日
     * @param date 日期对象
     * @return 网页日期字符串
     */
    public static String formatWebDate(Date date) {
        return formatDate(date, DateUtil.FORMAT_CN_YMD);
    }

    /**
     * 将日期对象格式化为网页日期时间格式，格式为 yyyy年MM月dd日 HH:mm
     * @param date 日期对象
     * @return 网页日期时间字符串
     */
    public static String formatWebDateTime(Date date) {
        return formatDate(date, DateUtil.FORMAT_CN_YMDHM);
    }

    /**
     * 将日期对象格式化为数据库日期格式，格式为 yyyy-MM-dd
     * 如果日期对象为 null，返回空字符串
     * @param date 日期对象
     * @return 数据库日期字符串
     */
    public static String formatDBDate(Date date) {
        return date == null ? "" : formatDate(date, DateUtil.FORMAT_YMD);
    }

    /**
     * 将日期对象格式化为数据库日期时间格式，格式为 yyyy-MM-dd HH:mm
     * 如果日期对象为 null，返回空字符串
     * @param date 日期对象
     * @return 数据库日期时间字符串
     */
    public static String formatDBDateTime(Date date) {
        return date == null ? "" : formatDate(date, DateUtil.FORMAT_YMDHMS);
    }

    /**
     * 获取指定日期的开始时间，格式为 yyyy-MM-dd 00:00:00
     * @param date 日期对象
     * @return 日期开始时间字符串
     */
    public static String formatDBDateStartTime(Date date) {
        return formatDBDate(date) + " 00:00:00";
    }

    /**
     * 获取指定日期的结束时间，格式为 yyyy-MM-dd 23:59:59
     * @param date 日期对象
     * @return 日期结束时间字符串
     */
    public static String formatDBDateEndTime(Date date) {
        return formatDBDate(date) + " 23:59:59";
    }

    
    /**
     * 按照指定格式获取当前日期字符串
     * @param format 日期格式
     * @return 当前日期字符串
     */
    public static String getDate(String format) {
        return formatDate(new Date(), format);
    }

    /**
     * 获取当前日期，格式为 yyyy-MM-dd
     * @return 当前日期字符串
     */
    public static String getDateNow() {
        sdf.get().applyPattern(DateUtil.FORMAT_YMD);
        return sdf.get().format(new Date());
    }

    /**
     * 获取当前网页日期，格式为 yyyy年MM月dd日
     * @return 当前网页日期字符串
     */
    public static String getWebDateNow() {
        sdf.get().applyPattern(DateUtil.FORMAT_CN_YMD);
        return sdf.get().format(new Date());
    }

    /**
     * 获取当前网页日期时间，格式为 yyyy年MM月dd日 HH:mm
     * @return 当前网页日期时间字符串
     */
    public static String getWebDateTimeNow() {
        sdf.get().applyPattern(DateUtil.FORMAT_CN_YMDHM);
        return sdf.get().format(new Date());
    }

    /**
     * 获取当前时间，格式为 HH:mm
     * @return 当前时间字符串
     */
    public static String getTimeNow() {
        sdf.get().applyPattern(DateUtil.FORMAT_HM);
        return sdf.get().format(new Date());
    }

    /**
     * 获取当前日期时间，格式为 yyyy-MM-dd HH:mm:ss
     * @return 当前日期时间字符串
     */
    public static String getDateTimeNow() {
        sdf.get().applyPattern(DateUtil.FORMAT_YMDHMS);
        return sdf.get().format(new Date());
    }

    /**
     * 获取当前数据库日期时间，格式为 yyyy-MM-dd HH:mm:ss
     * @return 当前数据库日期时间字符串
     */
    public static String getDBDateTimeNow() {
        return getDBDateTime(new Date());
    }

    /**
     * 获取当前数据库日期，格式为 yyyy-MM-dd
     * @return 当前数据库日期字符串
     */
    public static String getDBDateNow() {
        return getDBDate(new Date());
    }

    /**
     * 将日期对象格式化为数据库日期格式，格式为 yyyy-MM-dd
     * @param date 日期对象
     * @return 数据库日期字符串
     */
    public static String getDBDate(Date date) {
        sdf.get().applyPattern(DateUtil.FORMAT_YMD);
        return sdf.get().format(date);
    }

    /**
     * 将日期对象格式化为数据库日期时间格式，格式为 yyyy-MM-dd HH:mm:ss
     * @param date 日期对象
     * @return 数据库日期时间字符串
     */
    public static String getDBDateTime(Date date) {
        sdf.get().applyPattern(DateUtil.FORMAT_YMDHMS);
        return sdf.get().format(date);
    }


    /**
     * 获取指定日期若干天后的日期
     * @param date 日期字符串，格式为 yyyy-MM-dd
     * @param days 天数
     * @return 若干天后的日期字符串，格式为 yyyy-MM-dd
     */
    public static String getAfterDate(String date, int days) {
        Calendar cal = Calendar.getInstance();
        sdf.get().applyPattern(DateUtil.FORMAT_YMD);

        try {
            Date o = sdf.get().parse(date);
            cal.setTime(o);
            cal.add(5, days);
            return sdf.get().format(cal.getTime());
        } catch (ParseException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 计算两个日期之间的时间差
     * @param d1 第一个日期字符串
     * @param d2 第二个日期字符串
     * @param unit 时间单位，如 ONE_MINUTE、ONE_HOUR、ONE_DAY
     * @param format 日期格式，默认为 yyyy-MM-dd
     * @return 时间差，如果解析失败返回 -1
     */
    public static int getTimeDiff(String d1, String d2, int unit, String format) {
        Date date1 = null;
        Date date2 = null;
        int diff = -1;
        if (d1 != null && d2 != null) {
            if (format == null) {
                format = DateUtil.FORMAT_YMD;
            }

            sdf.get().applyPattern(format);

            try {
                date1 = sdf.get().parse(d1);
                date2 = sdf.get().parse(d2);
            } catch (ParseException e) {
                e.printStackTrace();
                return diff;
            }

            return getTimeDiff(date1, date2, unit);
        } else {
            return diff;
        }
    }

    /**
     * 计算两个日期对象之间的时间差
     * @param date1 第一个日期对象
     * @param date2 第二个日期对象
     * @param unit 时间单位，如 ONE_MINUTE、ONE_HOUR、ONE_DAY
     * @return 时间差，如果日期对象为 null 返回 -1
     */
    public static int getTimeDiff(Date date1, Date date2, int unit) {
        int diff = -1;
        if (date1 != null && date2 != null) {
            long millisecond = date1.getTime() - date2.getTime();
            switch (unit) {
                case 60000:
                    diff = (int)(millisecond / 60000L);
                    break;
                case 3600000:
                    diff = (int)(millisecond / 3600000L);
                    break;
                case 86400000:
                    diff = (int)(millisecond / 86400000L);
            }

            return Math.abs(diff);
        } else {
            return diff;
        }
    }

    /**
     * 将毫秒数转换为易读的时间间隔字符串
     * @param time 毫秒数
     * @return 易读的时间间隔字符串
     */
    public static String getElapsed(long time) {
        if (time > 31104000000L) {
            int year = (int)Math.floor((double)(time / 31104000000L));
            long m_diff = time % 31104000000L;
            if (m_diff > 2592000000L) {
                int month = (int)Math.floor((double)(m_diff / 2592000000L));
                return String.format("%d年 %d个月", year, month);
            } else {
                return String.format("%d年", year);
            }
        } else if (time > 2592000000L) {
            int month = (int)Math.floor((double)(time / 2592000000L));
            long d_diff = time % 2592000000L;
            if (d_diff > 86400000L) {
                int day = (int)Math.floor((double)(d_diff / 86400000L));
                return String.format("%d月 %d天", month, day);
            } else {
                return String.format("%d月", month);
            }
        } else if (time > 86400000L) {
            int day = (int)Math.floor((double)(time / 86400000L));
            long h_diff = time % 86400000L;
            if (h_diff > 3600000L) {
                int hour = (int)Math.floor((double)(h_diff / 3600000L));
                return String.format("%d天 %d小时", day, hour);
            } else {
                return String.format("%d天", day);
            }
        } else if (time > 3600000L) {
            int hour = (int)Math.floor((double)(time / 3600000L));
            long m_diff = time % 3600000L;
            if (m_diff > 60000L) {
                int minute = (int)Math.floor((double)(m_diff / 60000L));
                return String.format("%d 小时 %d 分钟", hour, minute);
            } else {
                return String.format("%d 小时", hour);
            }
        } else if (time > 60000L) {
            int minute = (int)Math.floor((double)(time / 60000L));
            return String.format("%d 分钟", minute);
        } else {
            return String.format("%d 秒", (int)(time / 1000L));
        }
    }

    /**
     * 计算指定时间距离当前时间的时间间隔，并返回易读的时间间隔字符串
     * @param time 指定时间的毫秒数
     * @return 易读的时间间隔字符串
     */
    public static String getTimeAgo(long time) {
        return getTimeAgo(time, (new Date()).getTime());
    }

    /**
     * 计算指定时间距离给定时间的时间间隔，并返回易读的时间间隔字符串
     * @param time 指定时间的毫秒数
     * @param now 给定时间的毫秒数
     * @return 易读的时间间隔字符串
     */
    public static String getTimeAgo(long time, long now) {
        long diff = Math.abs(now - time);
        if (diff > 31104000000L) {
            int year = (int)Math.floor((double)(diff / 31104000000L));
            long m_diff = diff % 31104000000L;
            if (m_diff > 2592000000L) {
                int month = (int)Math.floor((double)(m_diff / 2592000000L));
                return String.format("%d 年 %d 个月前", year, month);
            } else {
                return String.format("%d 年前", year);
            }
        } else if (diff > 2592000000L) {
            int month = (int)Math.floor((double)(diff / 2592000000L));
            long d_diff = diff % 2592000000L;
            if (d_diff > 86400000L) {
                int day = (int)Math.floor((double)(d_diff / 86400000L));
                return String.format("%d 月 %d 天前", month, day);
            } else {
                return String.format("%d 月前", month);
            }
        } else if (diff > 86400000L) {
            int day = (int)Math.floor((double)(diff / 86400000L));
            long h_diff = diff % 86400000L;
            if (h_diff > 3600000L) {
                int hour = (int)Math.floor((double)(h_diff / 3600000L));
                return String.format("%d 天 %d 小时前", day, hour);
            } else {
                return String.format("%d 天前", day);
            }
        } else if (diff > 3600000L) {
            int hour = (int)Math.floor((double)(diff / 3600000L));
            long m_diff = diff % 3600000L;
            if (m_diff > 60000L) {
                int minute = (int)Math.floor((double)(m_diff / 60000L));
                return String.format("%d 小时 %d 分钟前", hour, minute);
            } else {
                return String.format("%d 小时前", hour);
            }
        } else if (diff > 60000L) {
            int minute = (int)Math.floor((double)(diff / 60000L));
            return String.format("%d 分钟前", minute);
        } else {
            return String.format("%d 秒前", (int)(diff / 1000L));
        }
    }
}
